﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using EPiBoost.Core.Model;
using EPiServer;
using EPiServer.Core;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using EPiServer.Web.Routing;
using EPiBoost.Core.Extensions;
namespace EPiBoost.Core.Extensions.Html
{
	public static class NavigationExtensions
	{

		#region breadcrumb

		public static IEnumerable<NavigationLink> GetBreadcrumbNavigation(this HtmlHelper htmlHelper, bool includeCurrent = false, int maxParents = 0,
			IContent selectedNode = null,
			IContentLoader contentLoader = null,
			Func<IContent, bool> displayWhen = null
			)
		{
			contentLoader = contentLoader ?? ServiceLocator.Current.GetInstance<IContentLoader>();
			var languageName = htmlHelper.ViewContext.RequestContext.GetLanguage();
			var languageSelector = LanguageSelector.Fallback(languageName, true);
			selectedNode = selectedNode ?? contentLoader.Get<IContent>(htmlHelper.ViewContext.RequestContext.GetContentLink(), languageSelector);
			displayWhen = displayWhen ?? htmlHelper.VisibleToVisitor();

			if (maxParents == 0)
			{
				maxParents = int.MaxValue;
			}

			var result = new List<NavigationLink>();

			var currentNode = selectedNode;
			int i = 0;
			while (result.Count <= maxParents && selectedNode.ContentLink != PageReference.RootPage && !ContentReference.IsNullOrEmpty(currentNode.ParentLink))
			{
				if (i > 0 || (includeCurrent))
				{
					result.Add(new NavigationLink()
					           	{
					           		IsAncestorOfCurrentPage = true,
									Page = currentNode.GetPage(),
					           		IsCurrentPage = (i == 0),
					           		IsLast = result.Count == 0
					           	});
				}

				currentNode = contentLoader.Get<IContent>(currentNode.ParentLink, languageSelector);
				i++;

			}
			
			result.Reverse();

			return result;
		}



		#endregion


		#region tree


		public static IEnumerable<NavigationLink> GetTopLevelNavigation(this HtmlHelper htmlHelper)
		{
			return GetNavigationLinks(htmlHelper, PageReference.StartPage, false, 1, false);
		}


		public static IEnumerable<NavigationLink> GetNavigationLinks(this HtmlHelper htmlHelper, Type rootFinderType, bool expandAll, int maxTreeDepth = 0, bool includeRoot = false,
			IContent selectedNode = null,
			IContentLoader contentLoader = null,
			Func<IContent, bool> displayWhen = null
			)
		{
			contentLoader = contentLoader ?? ServiceLocator.Current.GetInstance<IContentLoader>();
			var languageName = htmlHelper.ViewContext.RequestContext.GetLanguage();
			var languageSelector = LanguageSelector.Fallback(languageName, true);
			selectedNode = selectedNode ?? contentLoader.Get<IContent>(htmlHelper.ViewContext.RequestContext.GetContentLink(), languageSelector);

			var rootPage = selectedNode.GetPage().FindFirstParentImplementingInterface(rootFinderType) ??
			               PageReference.StartPage.GetPage();
			return GetNavigationLinks(htmlHelper, rootPage.PageLink, includeRoot, maxTreeDepth, expandAll, selectedNode, contentLoader, displayWhen);
		}


		public static IEnumerable<NavigationLink> GetNavigationLinks(this HtmlHelper htmlHelper, PageReference root, bool includeRoot, int maxTreeDepth, bool expandAll,
			IContent selectedNode = null,  
            IContentLoader contentLoader = null, 
            Func<IContent, bool> displayWhen = null
			)
		{
			contentLoader = contentLoader ?? ServiceLocator.Current.GetInstance<IContentLoader>();
			var languageName = htmlHelper.ViewContext.RequestContext.GetLanguage();
			var languageSelector = LanguageSelector.Fallback(languageName, true);
			selectedNode = selectedNode ?? contentLoader.Get<IContent>(htmlHelper.ViewContext.RequestContext.GetContentLink(), languageSelector);
			displayWhen = displayWhen ?? htmlHelper.VisibleToVisitor();

			if (maxTreeDepth == 0)
			{
				maxTreeDepth = int.MaxValue;
			}

			var rootLink = GetTreeLinks(root, maxTreeDepth, expandAll, selectedNode, contentLoader, displayWhen, languageName, languageSelector);

			return includeRoot ? new List<NavigationLink>{rootLink} : rootLink.ChildNavigationLinks;
		}



		private static NavigationLink GetTreeLinks(PageReference root, int maxTreeDepth, bool expandAll, IContent selectedNode,
		                                       IContentLoader contentLoader, Func<IContent, bool> displayWhen, string languageName,
		                                       ILanguageSelector languageSelector)
		{
			var rootPage = contentLoader.Get<PageData>(root, languageSelector);
			var ancestors = contentLoader.GetSelfAndAncestors(rootPage, selectedNode, languageName);

			var rootLink = new Model.NavigationLink
			               	{
			               		Index = 0,
			               		Page = rootPage,
			               		TreeDepth = 0,
			               		IsCurrentPage = selectedNode.ContentLink == root
			               	};

			AddChildLinksRecursive(contentLoader, languageSelector, rootLink, 1, maxTreeDepth, expandAll, selectedNode, ancestors,
			                       displayWhen);
			return rootLink;
		}

		private static void AddChildLinksRecursive(IContentLoader contentLoader, ILanguageSelector languageSelector, Model.NavigationLink parentLink, int currentTreeDepth, int maxTreeDepth, bool expandAll, IContent selectedNode, IEnumerable<IContent> ancestors,  Func<IContent, bool> displayWhen)
		{
			if (currentTreeDepth <= maxTreeDepth)
			{
				var childPages = contentLoader.GetChildren<PageData>(parentLink.Page.PageLink, languageSelector);
				if(childPages!=null && childPages.Any())
				{ 
					int index = 0;

					foreach(var c in childPages)
					{
						if (displayWhen(c))
						{
							var childLink = new Model.NavigationLink()
							                	{
							                		Index = index,
							                		Page = c,
							                		TreeDepth = currentTreeDepth,
							                		IsCurrentPage = selectedNode.ContentLink == c.PageLink,
													IsAncestorOfCurrentPage = IsDescendant(ancestors, c.PageLink)
													
							                	};



							if (expandAll || IsDescendant(ancestors, c.ParentLink))
							{
								parentLink.ChildNavigationLinks.Add(childLink);
							}

							AddChildLinksRecursive(contentLoader, languageSelector, childLink, currentTreeDepth + 1, maxTreeDepth, expandAll,
							                       selectedNode, ancestors, displayWhen);
						}
						index++;
					}

					if (parentLink.HasChildNavigationLinks)
					{
						parentLink.ChildNavigationLinks.Last().IsLast = true;
					}
				}
			}
		}

		// todo : Refactor method out of this class
		private static bool IsDescendant(IEnumerable<IContent> ancestors, ContentReference contentLink )
		{
			return ancestors != null && ancestors.Any(x => x.ContentLink == contentLink);
		}

		#endregion
	}
}
